package com.demo01.server;

import com.demo01.core.DemoPacket;
import org.tio.core.ChannelContext;
import org.tio.core.TioConfig;
import org.tio.core.exception.TioDecodeException;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioHandler;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class ServerDemoHandler implements ServerAioHandler {

    /**
     * 解码 把接收到的ByteBuffer，解码成应用可以识别的业务消息包
     * 总的消息结构：  消息头 + 消息体
     * 消息头结构：   4个字节，存储消息体的长度
     * 消息体结构：   字节数组
     *
     * @param buffer 内存块缓冲区对象
     * @param limit  内存块中实际数据的容量大小
     * @param position       用来表示,在哪个位置开始从内存块读写数据
     * @param readableLength 累计可读的数据长度
     * @param channelContext 通道上下文
     * @return 数据包
     * @throws TioDecodeException 异常
     */
    @Override
    public Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws TioDecodeException {

        //当可读部分的长度比消息头的长度还小时，直接return null，然框架继续读取数据
        if (readableLength < DemoPacket.HEAD_LENGTH) {
            return null;
        }

        //从内存缓冲区中读取一个int数据(一个int占4个字节，正好就是我们之前定义的消息头)
        int bodyLength = buffer.getInt();

        //判断这个消息头的大小。理论上不可能小于0，所以直接异常抛出
        if (bodyLength < 0) {
            throw new TioDecodeException("bodyLength [" + bodyLength + "] is not right, remote:" + channelContext.getClientNode());
        }

        //此时，消息头的长度(固化了，就4个) + 实际内容体的长度 = 一个完整数据包的长度
        int neededLength = DemoPacket.HEAD_LENGTH + bodyLength;

        //判断框架累计读取的长度是否满足这个实际数据包的长度，如果不满足，让框架继续来获取数据
        int isDataEnough = readableLength - neededLength;
        if (isDataEnough < 0) {
            return null;
        } else {

        //    一切进行顺利，数据组装成业务包，return 出去。下一步到handler进行具体业务处理
            DemoPacket demoPacket = new DemoPacket();
            if (bodyLength > 0) {
        byte[] dst = new byte[bodyLength];
        buffer.get(dst);
        demoPacket.setBody(dst);
            }
            return demoPacket;
        }
    }

    /**
     * 编码   把业务消息包编码为可以发送的ByteBuffer
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：  4个字节，存储消息体的长度
     * 消息体结构：  字节数组
     *
     * @param packet 业务消息包
     * @param tioConfig      框架配置类
     * @param channelContext 通道上下文
     * @return 内存缓冲区对象
     */
    @Override
    public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
        //强类型转换
        DemoPacket demoPacket = (DemoPacket) packet;
        byte[] body = demoPacket.getBody();

        int bodyLen = 0;
        if (body != null) {
            bodyLen = body.length;
        }

        //根据实际内容大小创建一个内存缓冲区
        int allLen = DemoPacket.HEAD_LENGTH + bodyLen;
        ByteBuffer buffer = ByteBuffer.allocate(allLen);

        //设置ByteBuffer的字节序
        buffer.order(tioConfig.getByteOrder());

        //塞消息头
        buffer.putInt(bodyLen);

        if (body != null) {
        //    塞内容体
            buffer.put(body);
        }
        return buffer;
    }

    @Override
    public void handler(Packet packet, ChannelContext channelContext) throws Exception {
        DemoPacket demoPacket = (DemoPacket) packet;
        byte[] body = demoPacket.getBody();
        if (body != null) {
            String str = new String(body, StandardCharsets.UTF_8);
            System.out.println("收到消息：「" + str + "」");
        }
    }

}
